package cn.piesat.scanning.business.dqenvironment.single;

import cn.piesat.scanning.business.common.algorithm.processAlgorithm.SingleTaskAbstract;
import cn.piesat.scanning.business.dqenvironment.vo.RuleVO;
import cn.piesat.scanning.dto.*;
import cn.piesat.scanning.service.*;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.sql.Date;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xhl
 */
@Component
public class EnSingleTask extends SingleTaskAbstract {

    @Value("${scanning.data.day}")
    private Integer nearDay;

    private final DateTimeFormatter TIME_FORMAT =  DateTimeFormatter.ofPattern("yyyyMMddHHmmss");

    private static final String NULL = "NULL";
    /**
     * 同一谱段不同通道的时间差
     */
    private static final int DIFF_TIME_MINUTE = 3;

    private static final Logger logger = LoggerFactory.getLogger(EnSingleTask.class);

    @Autowired
    private HtScanningMetadataService htScanningMetadataService;
    @Autowired
    private DbDmsSchedulerTaskFileRecordService dbDmsSchedulerTaskFileRecordService;
    @Autowired
    private HtPathConfigurationService htPathConfigurationService;
    @Autowired
    private DbDmsScanningMetadataAuxInfoService dbDmsScanningMetadataAuxInfoService;
    @Autowired
    private DbDmsAlgorithmResultStoreService dbDmsAlgorithmResultStoreService;

    @Override
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams, HtScheduleJobDTO taskDTO) {
        AlgorithmTaskRuleParam taskRuleParam = JSON.parseObject(JSON.toJSONString(ruleParams), AlgorithmTaskRuleParam.class);
        AlgorithmTaskPathParam taskPathParam = JSON.parseObject(JSON.toJSONString(inputParams), AlgorithmTaskPathParam.class);
        //判断规则不为NULL但是输入路径为空的情况
        if (!rulePathExist(taskRuleParam,taskPathParam)) {
            return;
        }

        RuleVO primaryRuleVO = JSON.parseObject(taskRuleParam.getPrimaryFile(), RuleVO.class);
        //查找可执行的数据，默认查找配置文件中配置的时间文件，[状态为重做的文件]，记录表中不存在的主文件
        List<HtScanningMetadataDTO> execMetadataList = new ArrayList<>();
        execMetadataList.addAll(selectExecMainFileList(primaryRuleVO,taskDTO));
        execMetadataList.addAll(findRedoMetadataList(taskDTO.getId()));
        assemble(taskRuleParam,taskPathParam,execMetadataList,taskDTO);
    }

    private void assemble(AlgorithmTaskRuleParam taskRuleParam, AlgorithmTaskPathParam taskPathParam,
                          List<HtScanningMetadataDTO> execMetadataList, HtScheduleJobDTO taskDTO) {
        HtPathConfigurationDTO pathDTO = htPathConfigurationService.findById(taskDTO.getPathConfigId());
        RuleVO primaryRuleVO = JSON.parseObject(taskRuleParam.getPrimaryFile(), RuleVO.class);
        if(execMetadataList.size()<=0){
            logger.info("无数据");
        }
        for (HtScanningMetadataDTO metadataDTO : execMetadataList) {
            //匹配primaryFile规则
            String primaryFile = null;
                primaryFile = primaryRule(primaryRuleVO,  metadataDTO, pathDTO);
            if (primaryFile == null) {
                continue;
            }
            //匹配辅助数据文件规则
            RuleVO nwpRuleVO = JSON.parseObject(taskRuleParam.getNwpFile(), RuleVO.class);
            String nwpFile = "";
            if (StringUtils.isNotBlank(taskRuleParam.getNwpFile())) {
                nwpFile = nwpRule(nwpRuleVO, metadataDTO.getFileDate(), taskPathParam.getNwpFile());
                if (nwpFile == null) {
                    logger.debug("--[role-nwpFile]--:没有找到辅助数据C1D数据!");
                    continue;
                }
            }
            //匹配L2数据文件规则
            String l2File = l2Rule(taskRuleParam.getL2File(), metadataDTO, taskPathParam.getL2File());
            if (l2File == null) {
                logger.debug("--[role-L2]--:没有找到L2文件!");
                continue;
            }
            Map<String,Object> execParams = new HashMap<>();
            execParams.put("preprocess",false);
            execParams.put("algorithmId",taskDTO.getAlgoId());
            execParams.put("pathConfigId",taskDTO.getPathConfigId());
            execParams.put("fileDate",metadataDTO.getFileDate());
            execParams.put("lon",metadataDTO.getLon());
            execParams.put("lat",metadataDTO.getLat());
            execParams.put("taskPlanId",metadataDTO.getTaskPlanId());
            execParams.put("subPlanId",metadataDTO.getSubPlanId());
            execParams.put("viewNumber",metadataDTO.getViewNumber());
            execParams.put("productDir",pathDTO.getOutputPath());
            //页面配置的主文件参数的matchRule是文件种类
            execParams.put("fileCatalog",primaryRuleVO.getMatchRule());
            Map<String,Object> algorithmParams = new HashMap<>();
            algorithmParams.put("primaryFile",primaryFile);
            algorithmParams.put("L2File",l2File);
            algorithmParams.put("tcfPath",taskPathParam.getTcfPath());
            algorithmParams.put("resultPath",pathDTO.getOutputPath());
            algorithmParams.put("auxPath",pathDTO.getAssistPath());
            algorithmParams.put("nwpFile",nwpFile);
            String fileNme = metadataDTO.getFileName();
            //H8格式转换主文件是同一批次的多个文件，记录表里存的文件名称为HS_H08_20210112_0000_B08_FLDK_R20.DAT.bz2
            if(!("NULL").equals(primaryRuleVO.getFileNumber()) && ("H08").equals(primaryRuleVO.getMatchRule())){
                String [] str = fileNme.split("\\.");
                String s = str[1]+"."+str[2];
                fileNme = fileNme.substring(0,fileNme.lastIndexOf("_"))+"."+s;
                algorithmParams.put("mainFileName",fileNme);
            }else{
                algorithmParams.put("mainFileName",metadataDTO.getFileName());
            }
            updateRecordAndSendMessage(execParams,taskDTO,fileNme,metadataDTO.getFileDate(),algorithmParams);
        }
    }

    /**
     *
     * @param inputParams 输入参数
     * @param ruleParams 规则参数
     * @param taskDTO 任务信息对象
     * @param data 处理的数据对象，自己定义
     */
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams,
                                     HtScheduleJobDTO taskDTO, Object data) {
        HtScanningMetadataDTO metadataDTO = (HtScanningMetadataDTO)data;
        List<HtScanningMetadataDTO> dataList = new ArrayList<>();
        dataList.add(metadataDTO);
        AlgorithmTaskRuleParam taskRuleParam = JSON.parseObject(JSON.toJSONString(ruleParams), AlgorithmTaskRuleParam.class);
        AlgorithmTaskPathParam taskPathParam = JSON.parseObject(JSON.toJSONString(inputParams), AlgorithmTaskPathParam.class);
        assemble(taskRuleParam,taskPathParam,dataList,taskDTO);
    }
    /**
     * 校验输入规则的文件是否写入路径
     * @param ruleParam
     * @param pathParam
     * @return
     */
    private boolean rulePathExist(AlgorithmTaskRuleParam ruleParam, AlgorithmTaskPathParam pathParam) {
        if (StringUtils.isBlank(ruleParam.getPrimaryFile())) {
            logger.info("主文件规则为空");
            return false;
        }
        if (StringUtils.isNotBlank(ruleParam.getGeoFile())) {
            RuleVO geoRuleVO = JSON.parseObject(ruleParam.getGeoFile(), RuleVO.class);
            if (!NULL.equals(geoRuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getGeoFile())) {
                logger.info("geo文件输入路径为空");
                return false;
            }
        }
        if (StringUtils.isNotBlank(ruleParam.getL2File())) {
            RuleVO l2RuleVO = JSON.parseObject(ruleParam.getL2File(), RuleVO.class);
            if (!NULL.equals(l2RuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getL2File())) {
                logger.info("L2文件输入路径为空");
                return false;
            }
        }
        if (StringUtils.isNotBlank(ruleParam.getNwpFile())) {
            RuleVO nwpRuleVO = JSON.parseObject(ruleParam.getNwpFile(), RuleVO.class);
            if (!NULL.equals(nwpRuleVO.getMatchRule()) && StringUtils.isBlank(pathParam.getNwpFile())) {
                logger.info("辅助（nwp）文件输入路径为空");
                return false;
            }
        }
        return true;
    }

    protected List<HtScanningMetadataDTO> selectExecMainFileList(RuleVO primaryRuleVO,
                                                                 HtScheduleJobDTO taskDTO) {
        String mainMathexp = primaryRuleVO.getMatchRule().split(",|\\|")[0];
        String taskType = taskDTO.getTaskType();
        LocalDateTime now = LocalDateTime.now();
        String startTime = TIME_FORMAT.format(now.plusDays(-1L * nearDay));
        String endTime = TIME_FORMAT.format(now);
        if ("redo_singleTask".equals(taskType)) {
            Date redoStartTime = taskDTO.getRedoStartTime();
            Date redoEndTime = taskDTO.getRedoEndTime();
            startTime = TIME_FORMAT.format(redoStartTime.toInstant().atZone(ZoneId.systemDefault()));
            endTime = TIME_FORMAT.format(redoEndTime.toInstant().atZone(ZoneId.systemDefault()));
        }
        List<HtScanningMetadataDTO> allMetaDataList = null;
        allMetaDataList = htScanningMetadataService.listByFileCatalogAndDataLevelAndRangeTime(mainMathexp,primaryRuleVO.getLevel(),startTime,endTime)
        .stream().collect(Collectors.collectingAndThen
                        (Collectors.toCollection(() ->
                                        new TreeSet<>(Comparator.comparing(t -> t.getFileDate()))),
                                ArrayList::new
                        )
                );
        List<String> allNameList = allMetaDataList.stream().map(p->p.getFileName()).collect(Collectors.toList());
//        List<DbDmsSchedulerTaskFileRecordDTO> existRecordDataList = dbDmsSchedulerTaskFileRecordService.findByTaskId(taskDTO.getId());
        List<DbDmsSchedulerTaskFileRecordDTO> existRecordDataList = dbDmsSchedulerTaskFileRecordService.findByTaskIdAndFileNameIn(taskDTO.getId(),allNameList);
        List<HtScanningMetadataDTO> execMetadataList = null;
        //过滤需要执行的主文件，如果记录表中为空则查询出的时间范围内所有文件都执行，如果不为空，则筛选掉已经执行过的。
        if (existRecordDataList.isEmpty()) {
            execMetadataList = allMetaDataList;
        } else {
            Map<String, DbDmsSchedulerTaskFileRecordDTO> hash = existRecordDataList.stream().
                    collect(Collectors.toMap(DbDmsSchedulerTaskFileRecordDTO::getFileName, dto -> dto, (v1, v2) -> v2));
            if(!("NULL").equals(primaryRuleVO.getFileNumber()) && ("H08").equals(primaryRuleVO.getMatchRule())){
                execMetadataList = allMetaDataList.stream().filter(dto -> !hash.containsKey(dto.getFileName().substring(0,dto.getFileName().lastIndexOf("_"))+"."+dto.getFileName().split("\\.")[1]+"."+dto.getFileName().split("\\.")[2])).collect(Collectors.toList());
            }else{
                execMetadataList = allMetaDataList.stream().filter(dto -> !hash.containsKey(dto.getFileName())).collect(Collectors.toList());
            }
            logger.info("------execMetadataList-------"+execMetadataList.size());
        }
        return execMetadataList;
    }

    protected List<HtScanningMetadataDTO> findRedoMetadataList(String taskId) {
        List<DbDmsSchedulerTaskFileRecordDTO> redoTaskRecordList = dbDmsSchedulerTaskFileRecordService.findRedoTasks(taskId);
        List<String> fileNameList = redoTaskRecordList.stream()
                .map(DbDmsSchedulerTaskFileRecordDTO::getFileName).collect(Collectors.toList());
        return htScanningMetadataService.findByFileNameList(fileNameList);
    }

        private String primaryRule(RuleVO primaryFileRule,  HtScanningMetadataDTO mainFileDTO,
                               HtPathConfigurationDTO pathDTO) {
        //主文件规则时间范围
        String mainFileDate = mainFileDTO.getFileDate();
        Map<String,String> timeRangeParam = parseTimeRange(mainFileDate, primaryFileRule.getTimeRange());
           List<HtScanningMetadataDTO> metList = htScanningMetadataService.findByFileCatalogAndDataLevelAndFileDate(mainFileDTO.getFileCatalog(),mainFileDTO.getDataLevel(),mainFileDTO.getFileDate());
            //判断磁盘上是否存在文件
            StringBuilder sb = new StringBuilder();
            for (HtScanningMetadataDTO dto : metList) {
                String currentFilePath = pathDTO.getClientPath() + File.separator + dto.getFilePath();
                File file = new File(currentFilePath);
                if (!file.exists()) {
                    logger.info("{}--->文件不存在",file.getName());
                    return null;
                }
                sb.append(",").append(currentFilePath);
            }
            return sb.substring(1);
    }
    private String l2Rule(String rule, HtScanningMetadataDTO mainFileDTO,String l2PathPrefix) {
        StringBuilder filePathSb = new StringBuilder();
        if (StringUtils.isBlank(rule)) {
            return filePathSb.toString();
        }
        RuleVO l2RuleVO = JSON.parseObject(rule,RuleVO.class);
        if (NULL.equals(l2RuleVO.getMatchRule()) || NULL.equals(l2RuleVO.getLevel())) {
            return filePathSb.toString();
        }
        Map<String,String> timeRangeParam = parseTimeRange(mainFileDTO.getFileDate(), l2RuleVO.getTimeRange());
        String[] mathexpArr = l2RuleVO.getMatchRule().split("\\|");
        List<DbDmsAlgorithmResultStoreDTO> ruleFileList = new ArrayList<>();
        String startTime = timeRangeParam.get("startTime");
        String endTime = timeRangeParam.get("endTime");
        String fileType = "NC";
        if (startTime.equals(endTime)) {
            ruleFileList = dbDmsAlgorithmResultStoreService
                    .findByDataLevelAndProductIdentifyInAndFileDateBetweenAndFiletype(Arrays.asList(mathexpArr), startTime,endTime,l2RuleVO.getLevel(),fileType);
        }
        Map<String, List<DbDmsAlgorithmResultStoreDTO>> filterMap = ruleFileList.stream()
                .collect(Collectors.groupingBy(DbDmsAlgorithmResultStoreDTO::getProductIdentify));
        if (filterMap.size() != mathexpArr.length) {
            logger.info("与主文件{}要求的L2级文件数量与L2级规则产品标识数量不一致",mainFileDTO.getFileName());
            return null;
        }
        for (DbDmsAlgorithmResultStoreDTO fileDTO : ruleFileList) {
            String currentPath = l2PathPrefix + File.separator + fileDTO.getFilepath();
            File file = new File(currentPath);
            if (!file.exists()) {
                return null;
            }
            filePathSb.append(",").append(currentPath);
        }
        return filePathSb.substring(1);
    }
    private String nwpRule(RuleVO nwpRuleVO, String mainFileDate, String npwPathPrefix) {
        StringBuilder filePathSb = new StringBuilder();
        if(nwpRuleVO.getMatchRule().equals("ECMWF_C1D_SURF") && !NULL.equals(nwpRuleVO.getTimeRange())){
             //去辅助源数据表里找预报时间（FORECAST_DATE）最靠近mainFileDate的数据,并且起报时间（file_date）为timeRange[0]之间的数据，
            // 从中选取起报时间最靠近预报时间的一条数据
            String timeRange =  nwpRuleVO.getTimeRange();
            Map<String,String> timeRangeParam = parseTimeRange(mainFileDate, timeRange);
            String startTime = timeRangeParam.get("startTime");
            String fileCatalog = nwpRuleVO.getMatchRule();
            //获取预报时间最靠近主文件fileDate的一条辅助数据，取其中的预报时间，再查询起报时间最靠近预报时间的一条数据
            List<DbDmsScanningMetadataAuxInfoDTO> list = dbDmsScanningMetadataAuxInfoService.findByFileCatalogAndForecastDateBetweenOrderByForecastDateDesc(fileCatalog,startTime,mainFileDate);
            if(list.size()==0 || list ==null){
                return null;
            }
            String forecastDate = list.get(0).getForecastDate();
            List<DbDmsScanningMetadataAuxInfoDTO> list1 = dbDmsScanningMetadataAuxInfoService.findByFileCatalogAndForecastDateOrderByFileDateDesc(fileCatalog,forecastDate);
            if(list1.size()>0 && list1 !=null){
                DbDmsScanningMetadataAuxInfoDTO  auxDto= list1.get(0);
                String currentPath = npwPathPrefix + File.separator + auxDto.getFilePath();
                File file = new File(currentPath);
                if (!file.exists()) {
                    logger.info("{}--->nwp文件在磁盘上不存在",file.getName());
                    return null;
                }
                filePathSb.append(currentPath);
            }else{
                return null;
            }
        }
        return filePathSb.toString();
    }

    private Map<String,String> parseTimeRange(String mainFileDate,String timeRange) {
        Map<String,String> resultMap = new HashMap<>();
        resultMap.put("startTime",mainFileDate);
        resultMap.put("endTime",mainFileDate);
        if (timeRange.equals("NULL")) {
            return resultMap;
        }
        String[] timeSplit = timeRange.split(",");
        long startPlusMinute = -1 * Math.abs(Long.parseLong(timeSplit[0]));
        long endPlusMinute = Math.abs(Long.parseLong(timeSplit[1]));
        LocalDateTime mainFileLocalDateTime = LocalDateTime.parse(mainFileDate, TIME_FORMAT);
        String startTime = TIME_FORMAT.format(mainFileLocalDateTime.plusMinutes(startPlusMinute));
        String endTime = TIME_FORMAT.format(mainFileLocalDateTime.plusMinutes(endPlusMinute));
        resultMap.put("startTime",startTime);
        resultMap.put("endTime",endTime);
        return resultMap;
    }
    @Override
    public void parseRuleAndFindData(Map<String, Object> inputParams, Map<String, Object> ruleParams, Map<String, Integer> indexParams, HtScheduleJobDTO taskDTO) {
        // TODO Auto-generated method stub
    }
}
